package com.tiny.tinyweb.core.aop;

import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.commons.logging.Log;
import org.springframework.aop.interceptor.AbstractTraceInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;

import com.tiny.tinyweb.core.dao.entity.MapEntity;
import com.tiny.tinyweb.core.dao.xaccess.JdbcExecute;
import com.tiny.tinyweb.core.log.LogInfo;
import com.tiny.tinyweb.core.utils.ConversionUtils;
import com.tiny.tinyweb.core.utils.FacesUtils;
import com.tiny.tinyweb.core.utils.ReflectionUtils;
import com.tiny.tinyweb.core.utils.StringUtils;

/**
 * 基于{@link org.springframework.aop.interceptor.AbstractTraceInterceptor}实现的
 * SQL执行状态的跟踪器类，用来将SQL语句和参数输出到调试日志。
 *
 * @see {@link org.springframework.aop.interceptor.AbstractTraceInterceptor}
 * @author wxiaobin
 * @version 1.0
 * @since 1.0
 */
public class SqlLogInterceptor extends AbstractTraceInterceptor {

    private static final long serialVersionUID = 1L;

    @Autowired
    private JdbcExecute jdbcExecute;
    public void setJdbcExecute(JdbcExecute jdbcExecute) {
        this.jdbcExecute = jdbcExecute;
    }

    /**
     * 跟踪目标方法的执行，将SQL语句和参数输出到调试日志。
     *
     * @param invocation 由{@link org.aopalliance.intercept.MethodInvocation}包装的方法调用对象
     * @param logger 日志输出对象，通过loggerName来使用指定的类名创建
     * @return 目标方法的返回值
     */
    @Override
    protected Object invokeUnderTrace(MethodInvocation invocation, Log logger) throws Throwable {
        // 跳过“toString”、“toJSON”和“hashCode”方法
        String method = invocation.getMethod().getName();
        if ("toString".equals(method) || "toJSON".equals(method) || "hashCode".equals(method)) {
            return invocation.proceed();
        }

        // 创建一个保存日志信息的对象
        LogInfo logInfo = new LogInfo();
        // 如果当前环境是JSF上下文，从请求中获得用户的登录信息
        if (FacesContext.getCurrentInstance() != null) {
            HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance()
                    .getExternalContext().getRequest();
            // 获得既有Session，不执行创建处理
            HttpSession session = request == null ? null : request.getSession(false);
            // 获得SessionID
            if (session != null) {
                logInfo.setSessionID(session.getId());
            }
            // 获得登录用户IP地址
            logInfo.setOperatorIP(FacesUtils.getIpAddr(request));
            // 获得登录用户名
            MapEntity userInfo = (MapEntity) FacesUtils.getSessionUser();
            if (userInfo != null && userInfo.get("userNo") != null) {
                logInfo.setOperatorID((String) userInfo.get("userNo"));
            }
        }
        // 获得目标方法名称
        logInfo.setOperation(method);

        // 获得目标方法参数信息
        Object[] args = invocation.getArguments();
        // 获得由Spring的JdbcTemplate执行的SQL信息
        String sql = "";
        StringBuilder params = new StringBuilder();
        try {
            if (args.length >= 1) {
                // 非批量更新时，从第一个参数中获得SQL信息
                if (args[0] instanceof PreparedStatementCreator) {
                    // 实际执行的SQL语句
                    sql = (String) ReflectionUtils.getPropertyValue(args[0], "actualSql");
                    // SQL语句的执行参数
                    params.append(ToStringBuilder.reflectionToString(
                            ReflectionUtils.getPropertyValue(args[0], "parameters"), ToStringStyle.SIMPLE_STYLE));
                }
                // 批量更新时，SQL语句在第一个参数中，SQL语句的执行参数被封装在第二个参数中
                if ("batchUpdate".equals(method)) {
                    // SQL语句
                    sql = (String) args[0];
                    // SQL语句的执行参数
                    if (args.length >= 2) {
                        // 暴力访问私有字段
                        SqlParameterSource[] sps = (SqlParameterSource[]) ReflectionUtils
                                .getPropertyValue(args[1], "val$batchArgs");
                        // 循环处理所有的批处理参数，各组参数用逗号连接在一起
                        for (int i = 0; i < sps.length; i++) {
                            params.append(ToStringBuilder.reflectionToString(
                                    ((MapSqlParameterSource) sps[i]).getValues(), ToStringStyle.SIMPLE_STYLE));
                            if (i != sps.length - 1) {
                                params.append(",");
                            }
                        }
                    }
                }
            }
            // 将SQL语句输出到调试日志
            logInfo.setMessage("sql=[%1$s]", ConversionUtils.multiToSingle(sql, ' ', " "));
            logger.debug(logInfo.toString());
            // 将SQL语句的执行参数输出到调试日志
            logInfo.setMessage("params=[%1$s]", params.toString());
            logger.debug(logInfo.toString());
        } catch (Exception e) {
            // 从Spring的JdbcTemplate中获取SQL信息失败时，输出警告日志
            logInfo.setMessage("Can not get SQL statement from JdbcTemplate. [%1$s]", e.getMessage());
            logger.warn(logInfo.toString());
        }

        // 调用目标方法，返回目标方法的返回值
        return invocation.proceed();
    }

    @Override
    protected boolean isLogEnabled(Log logger) {
        return logger.isErrorEnabled();
    }
}
